"""
重试处理工具
提供API调用的错误处理和重试机制。
"""

import asyncio
import logging
from typing import Callable, Any, Optional, Dict, List
from functools import wraps
import time
from datetime import datetime, timedelta

logger = logging.getLogger(__name__)


class RetryConfig:
    """重试配置类"""
    
    def __init__(
        self,
        max_retries: int = 3,
        base_delay: float = 1.0,
        max_delay: float = 60.0,
        exponential_base: float = 2.0,
        jitter: bool = True
    ):
        """
        初始化重试配置
        
        Args:
            max_retries: 最大重试次数
            base_delay: 基础延迟时间（秒）
            max_delay: 最大延迟时间（秒）
            exponential_base: 指数退避基数
            jitter: 是否添加随机抖动
        """
        self.max_retries = max_retries
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.exponential_base = exponential_base
        self.jitter = jitter


class RetryableError(Exception):
    """可重试的错误"""
    pass


class NonRetryableError(Exception):
    """不可重试的错误"""
    pass


class RetryHandler:
    """重试处理器"""
    
    def __init__(self, config: RetryConfig = None):
        """
        初始化重试处理器
        
        Args:
            config: 重试配置
        """
        self.config = config or RetryConfig()
        self.retry_stats: Dict[str, Dict] = {}
    
    def calculate_delay(self, attempt: int) -> float:
        """
        计算延迟时间
        
        Args:
            attempt: 当前尝试次数
            
        Returns:
            float: 延迟时间（秒）
        """
        # 指数退避
        delay = self.config.base_delay * (self.config.exponential_base ** attempt)
        
        # 限制最大延迟
        delay = min(delay, self.config.max_delay)
        
        # 添加随机抖动
        if self.config.jitter:
            import random
            jitter_range = delay * 0.1
            delay += random.uniform(-jitter_range, jitter_range)
        
        return max(0, delay)
    
    def is_retryable_error(self, error: Exception) -> bool:
        """
        判断错误是否可重试
        
        Args:
            error: 异常对象
            
        Returns:
            bool: 是否可重试
        """
        # 明确标记的错误类型
        if isinstance(error, NonRetryableError):
            return False
        
        if isinstance(error, RetryableError):
            return True
        
        # 网络相关错误通常可重试
        error_str = str(error).lower()
        retryable_patterns = [
            'timeout',
            'connection',
            'network',
            'temporary',
            'rate limit',
            'server error',
            '502',
            '503',
            '504'
        ]
        
        return any(pattern in error_str for pattern in retryable_patterns)
    
    async def execute_with_retry(
        self,
        func: Callable,
        *args,
        operation_name: str = "unknown",
        **kwargs
    ) -> Any:
        """
        执行带重试的异步函数
        
        Args:
            func: 要执行的函数
            *args: 函数参数
            operation_name: 操作名称（用于日志）
            **kwargs: 函数关键字参数
            
        Returns:
            Any: 函数执行结果
            
        Raises:
            Exception: 最后一次执行的异常
        """
        last_error = None
        start_time = datetime.now()
        
        # 初始化统计信息
        if operation_name not in self.retry_stats:
            self.retry_stats[operation_name] = {
                'total_attempts': 0,
                'successful_attempts': 0,
                'failed_attempts': 0,
                'total_retries': 0
            }
        
        stats = self.retry_stats[operation_name]
        
        for attempt in range(self.config.max_retries + 1):
            try:
                stats['total_attempts'] += 1
                
                # 执行函数
                if asyncio.iscoroutinefunction(func):
                    result = await func(*args, **kwargs)
                else:
                    result = func(*args, **kwargs)
                
                # 成功执行
                stats['successful_attempts'] += 1
                
                if attempt > 0:
                    duration = (datetime.now() - start_time).total_seconds()
                    logger.info(
                        f"操作 '{operation_name}' 在第 {attempt + 1} 次尝试后成功，"
                        f"总耗时: {duration:.2f}秒"
                    )
                
                return result
                
            except Exception as error:
                last_error = error
                stats['failed_attempts'] += 1
                
                # 检查是否可重试
                if not self.is_retryable_error(error):
                    logger.error(f"操作 '{operation_name}' 遇到不可重试错误: {error}")
                    raise error
                
                # 检查是否还有重试机会
                if attempt >= self.config.max_retries:
                    logger.error(
                        f"操作 '{operation_name}' 在 {attempt + 1} 次尝试后仍然失败: {error}"
                    )
                    break
                
                # 计算延迟时间并等待
                delay = self.calculate_delay(attempt)
                stats['total_retries'] += 1
                
                logger.warning(
                    f"操作 '{operation_name}' 第 {attempt + 1} 次尝试失败: {error}，"
                    f"{delay:.2f}秒后重试"
                )
                
                await asyncio.sleep(delay)
        
        # 所有重试都失败了
        raise last_error
    
    def get_retry_stats(self, operation_name: Optional[str] = None) -> Dict:
        """
        获取重试统计信息
        
        Args:
            operation_name: 操作名称，如果为None则返回所有统计
            
        Returns:
            Dict: 统计信息
        """
        if operation_name:
            return self.retry_stats.get(operation_name, {})
        return self.retry_stats.copy()
    
    def reset_stats(self, operation_name: Optional[str] = None):
        """
        重置统计信息
        
        Args:
            operation_name: 操作名称，如果为None则重置所有统计
        """
        if operation_name:
            if operation_name in self.retry_stats:
                del self.retry_stats[operation_name]
        else:
            self.retry_stats.clear()


def retry_on_failure(
    max_retries: int = 3,
    base_delay: float = 1.0,
    max_delay: float = 60.0,
    exponential_base: float = 2.0,
    jitter: bool = True,
    operation_name: str = None
):
    """
    重试装饰器
    
    Args:
        max_retries: 最大重试次数
        base_delay: 基础延迟时间
        max_delay: 最大延迟时间
        exponential_base: 指数退避基数
        jitter: 是否添加随机抖动
        operation_name: 操作名称
    """
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            config = RetryConfig(
                max_retries=max_retries,
                base_delay=base_delay,
                max_delay=max_delay,
                exponential_base=exponential_base,
                jitter=jitter
            )
            
            handler = RetryHandler(config)
            op_name = operation_name or func.__name__
            
            return await handler.execute_with_retry(
                func, *args, operation_name=op_name, **kwargs
            )
        
        return wrapper
    return decorator


# 全局重试处理器实例
default_retry_handler = RetryHandler()